import Axios, { AxiosRequestConfig, AxiosResponse, AxiosError } from 'axios'
import { message as Message } from 'ant-design-vue'
import { successCode, specialCase, tokenExpires, businessError } from './responseCode'
// import router from '@/router'
// import qs from 'qs'
import store from '@/store'
import { setToken, getToken, removeToken } from '@/utils/auth'
import { refreshToken } from '@/api/system/user'
import NProgress from 'nprogress'
const RESPONSE_STATUS = {
  OK: 200,
  NOT_LOGIN: 401,
  NO_PERMISSON: 403,
  NOT_FOUND: 404,
  SERVER_ERROR: 500,
}

interface CustomAxiosRequestConfig extends AxiosRequestConfig {
  hideLoading?: boolean
}

export interface BaseResponse<T = any> {
  code: number
  data: T
  message: string
}

const service = Axios.create({
  baseURL: (import.meta.env.VITE_API_DOMAIN as string) || undefined,
  timeout: 50000,
})

service.interceptors.request.use(
  (config: AxiosRequestConfig) => {
    NProgress.start()

    config.headers.token = getToken()
    // eslint-disable-next-line no-param-reassign
    // config.data = qs.stringify(config.data) // 转为 formdata 数据格式
    return config
  },
  (error: { message: string }) => {
    Message.error(error.message)
  },
)

// 防止多个接口同时调用刷新接口
let isRefreshing = false
// 需要重试的接口
let requests: (() => void)[] = []
service.interceptors.response.use(
  async (response: AxiosResponse): Promise<any> => {
    const { data } = response
    const { code, message } = data

    NProgress.done()
    switch (true) {
      case code === tokenExpires: // token失效处理
        if (!isRefreshing) {
          isRefreshing = true
          try {
            const oldToken = sessionStorage.getItem('refreshToken')
            const res = await refreshToken({ refreshToken: oldToken })
            // @ts-ignore
            const { token } = res

            setToken(token)
            store.commit('user/setToken', token)
            // @ts-ignore
            sessionStorage.setItem('refreshToken', res.refreshToken)
            // token 刷新后将数组的方法重新执行
            requests.forEach((cb) => cb())
            requests = [] // 重新请求完清空
            return service(response.config)
          } catch (error) {
            console.log('response-416-error', error)
            store.commit('user/setToken', '')
            removeToken()
          } finally {
            isRefreshing = false
          }
        } else {
          // 返回未执行 resolve 的 Promise
          return new Promise((resolve) => {
            // 用函数形式将 resolve 存入，等待刷新后再执行
            requests.push(() => {
              resolve(service(response.config))
            })
          })
        }
        return response.data
      case businessError.includes(code): // 业务错误全局自动提示弹出 返回reject交给后续业务处理
        Message.warning(message || '业务错误')
        return Promise.reject(data)
      case specialCase.includes(code): // 不需要自动弹出提示 直接返回reject
        return Promise.reject(data)
      case code === successCode: // 成功状态吗 返回 resolve
        return Promise.resolve(data)
      default:
        // 其他奇怪的的状态码兜底
        return Promise.reject(data)
    }
  },
  (error: AxiosError) => {
    NProgress.done()
    Message.destroy()
    const response = Object.assign({}, error.response)
    if (response) {
      switch (response.status) {
        case RESPONSE_STATUS.NOT_FOUND:
          Message.error('没找到接口信息')
          break
        case RESPONSE_STATUS.SERVER_ERROR:
          Message.error('系统异常')
          break
        default:
          Message.error(`连接错误-${response.status}`)
      }
    }
    return Promise.reject(error)
  },
)

const request = <T = any>(config: CustomAxiosRequestConfig): Promise<T> => {
  return new Promise((resolve, reject) => {
    service
      .request<BaseResponse<T>>(config)
      .then((res: AxiosResponse) => resolve(res.data))
      .catch((err: { message: string }) => reject(err))
  })
}

request.get = <T = any>(url: string, params?: object): Promise<T> =>
  request({
    method: 'get',
    url,
    params,
  })

request.post = <T = any>(url: string, params?: object): Promise<T> =>
  request({
    method: 'post',
    url,
    data: params,
  })

request.delete = <T = any>(url: string, params?: object): Promise<T> =>
  request({
    method: 'delete',
    url,
    params,
  })

request.put = <T = any>(url: string, params?: object): Promise<T> =>
  request({
    method: 'put',
    url,
    data: params,
  })

request.patch = <T = any>(url: string, params?: object): Promise<T> =>
  request({
    method: 'patch',
    url,
    data: params,
  })

export default request
